# 市场变化检测
# src/algorithms/change_detection.py
import numpy as np
import pandas as pd
from scipy import stats
from typing import Tuple, Dict, Any
from datetime import datetime, timedelta
import warnings

class MarketChangeDetector:
    """市场变化检测器"""
    
    def __init__(self, window_size: int = 30, alpha: float = 0.05):
        self.window_size = window_size
        self.alpha = alpha
        self.history = []
        
    def ks_test(self, current_returns: np.ndarray, 
                historical_returns: np.ndarray) -> Tuple[bool, float]:
        """
        使用K-S检验检测市场变化
        
        Args:
            current_returns: 当前窗口的收益率
            historical_returns: 历史窗口的收益率
            
        Returns:
            (是否变化, p值)
        """
        try:
            # 执行K-S检验
            statistic, p_value = stats.ks_2samp(current_returns, historical_returns)
            
            # 判断是否发生显著变化
            is_change = p_value < self.alpha
            
            return is_change, p_value
            
        except Exception as e:
            warnings.warn(f"K-S检验失败: {e}")
            return False, 1.0
    
    def bayes_changepoint_detection(self, returns: np.ndarray) -> Dict[str, Any]:
        """
        贝叶斯变化点检测
        
        Args:
            returns: 收益率序列
            
        Returns:
            检测结果字典
        """
        try:
            # 简化的贝叶斯变化点检测
            n = len(returns)
            changepoints = []
            
            # 使用滑动窗口检测变化点
            for i in range(self.window_size, n - self.window_size):
                before = returns[i-self.window_size:i]
                after = returns[i:i+self.window_size]
                
                # 计算均值和方差的差异
                mean_diff = abs(np.mean(before) - np.mean(after))
                var_diff = abs(np.var(before) - np.var(after))
                
                # 简单的阈值判断
                if mean_diff > 2 * np.std(returns) or var_diff > 2 * np.var(returns):
                    changepoints.append(i)
            
            return {
                'changepoints': changepoints,
                'num_changes': len(changepoints),
                'probability': len(changepoints) / n
            }
            
        except Exception as e:
            warnings.warn(f"贝叶斯变化点检测失败: {e}")
            return {'changepoints': [], 'num_changes': 0, 'probability': 0.0}
    
    def detect_market_change(self, returns_matrix: np.ndarray, 
                           method: str = "ks") -> Dict[str, Any]:
        """
        检测市场变化
        
        Args:
            returns_matrix: 收益率矩阵 (时间 x 资产)
            method: 检测方法 ("ks" 或 "bayes")
            
        Returns:
            检测结果
        """
        if len(returns_matrix) < 2 * self.window_size:
            return {
                'is_change': False,
                'p_value': 1.0,
                'trigger_at': datetime.now(),
                'method': method
            }
        
        # 分割当前窗口和历史窗口
        current_window = returns_matrix[-self.window_size:]
        historical_window = returns_matrix[-2*self.window_size:-self.window_size]
        
        if method == "ks":
            # 对每个资产进行K-S检验
            p_values = []
            for i in range(returns_matrix.shape[1]):
                _, p_val = self.ks_test(current_window[:, i], historical_window[:, i])
                p_values.append(p_val)
            
            # 使用最小p值判断
            min_p_value = min(p_values)
            is_change = min_p_value < self.alpha
            
            return {
                'is_change': is_change,
                'p_value': min_p_value,
                'trigger_at': datetime.now(),
                'method': method,
                'asset_p_values': p_values
            }
            
        elif method == "bayes":
            # 贝叶斯检测
            result = self.bayes_changepoint_detection(returns_matrix.flatten())
            
            return {
                'is_change': result['probability'] > 0.1,
                'p_value': 1 - result['probability'],
                'trigger_at': datetime.now(),
                'method': method,
                'changepoints': result['changepoints']
            }
        
        else:
            raise ValueError(f"不支持的检测方法: {method}")
